/**
 * Copyright (c) 2019 Coder League
 * All rights reserved.
 *
 * File：OperateLogAspect.java
 * History:
 *         2019年5月11日: Initially created, CJH.
 */
package club.coderleague.ilsp.common.aspect;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.AnnotatedElementUtils;
import org.springframework.stereotype.Component;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ResponseBody;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;

import club.coderleague.ilsp.common.annotation.OperateLog;
import club.coderleague.ilsp.common.domain.beans.ResultMessage;
import club.coderleague.ilsp.common.domain.beans.UserSession;
import club.coderleague.ilsp.common.domain.enums.EntityState;
import club.coderleague.ilsp.common.domain.enums.ResultCode;
import club.coderleague.ilsp.common.exception.MessageException;
import club.coderleague.ilsp.entities.Operatelogs;
import club.coderleague.ilsp.service.operatelogs.OperateLogsService;
import club.coderleague.ilsp.util.SessionUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 操作日志切面记录
 * 
 * @author CJH
 */
@Slf4j
@Aspect
@Component
public class OperateLogAspect {
	/**
	 * 切入点
	 */
	private final String POINT_CUT = "@within(org.springframework.stereotype.Controller)";
	
	/**
	 * 操作日志Service
	 */
	private @Autowired OperateLogsService operateLogsService;
	
	/**
	 * 定义切点
	 * 
	 * @author CJH 2019年5月11日
	 * @param operateLog 日志描述
	 */
	@Pointcut(POINT_CUT)
	public void pointCut() {}
	
	/**
	 * 操作日志记录
	 * 
	 * @author CJH 2019年5月11日
	 * @param pjp 切入点信息
	 * @return 切入方法返回数据
	 */
	@Around("pointCut()")
	public Object around(ProceedingJoinPoint pjp) {
		// 响应数据
	    Object proceedresult = null;
	    // 状态
	    boolean status = true;
	    // 异常信息
	    Object exceptionresult = null;
	    try {
	    	// 执行方法
	    	proceedresult = pjp.proceed();
	    } catch (ConstraintViolationException e) {
	    	// 参数校验异常
	    	log.error(e.getMessage(), e);
	    	status = false;
	    	exceptionresult = new ResultMessage(ResultCode.WARN.getValue(), e.getConstraintViolations().stream()
	    			.map(cv -> cv == null ? "null" : cv.getMessage()).collect(Collectors.joining(", ")));
		} catch (ValidationException e) {
	    	// 数据校验异常
	    	log.error(e.getMessage(), e);
	    	status = false;
	    	exceptionresult = new ResultMessage(ResultCode.WARN.getValue(), e.getMessage());
		} catch (MessageException e) {
			// 方法逻辑处理异常
	    	log.error(e.getMessage(), e);
	        status = false;
	    	exceptionresult = new ResultMessage(e.getResultcode().getValue(), e.getMessage());
		} catch (Throwable e) {
			// 未知异常
	    	log.error(e.getMessage(), e);
	        status = false;
	        exceptionresult = "系统错误";
	    }
	    MethodSignature methodSignature = (MethodSignature)pjp.getSignature();
        Method method = methodSignature.getMethod();
	    if (!status) {
	    	// 异常统一返回数据
	        if (method.isAnnotationPresent(ResponseBody.class)) {
	        	proceedresult = new ModelMap().addAttribute("status", status).addAttribute("result", exceptionresult);
		    } else {
		    	proceedresult = "/500";
		    }
	    }
	    saveOperatelogs(pjp, status, proceedresult);
	    return proceedresult;
	}
	
	/**
	 * 保存操作日志
	 * 
	 * @author CJH 2019年6月21日
	 * @param pjp 切入点信息
	 * @param returnresult 返回结果状态
	 * @param resultObj 返回数据
	 */
	public void saveOperatelogs(ProceedingJoinPoint pjp, boolean returnresult, Object resultObj) {
		try {
			// 对象序列化
		    ObjectMapper mapper = new ObjectMapper();
		    // 操作日志
		    Operatelogs operatelogs = getOperatelogs(pjp);
			if (operatelogs != null) {
				// 保存操作日志
				operatelogs.setReturnresult(returnresult);
				// 设置响应数据
				operatelogs.setResponsedata(resultObj != null ? mapper.writeValueAsString(resultObj) : null);
				operateLogsService.save(operatelogs);
			}
		} catch (Exception e) {
			log.error("保存操作日志发生错误-->" + e.getMessage(), e);
		}
	}
	
	/**
	 * 获取操作日志
	 * 
	 * @author CJH 2019年5月11日
	 * @param joinPoint 切入点信息
	 * @return 操作日志
	 * @throws JsonProcessingException 对象转换JSON字符串异常
	 */
	public Operatelogs getOperatelogs(ProceedingJoinPoint joinPoint) throws JsonProcessingException {
		Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (!AnnotatedElementUtils.isAnnotated(method, OperateLog.class)) {
        	return null;
        }
		// 对象序列化
		ObjectMapper mapper = new ObjectMapper();
		// 请求信息
		HttpServletRequest request = SessionUtil.getHttpServletRequest();
        // 获取用户信息
        UserSession us = SessionUtil.getUserSession();
        Long operator = us != null ? us.getUserid() : null;
        Integer usergroup = us != null ? us.getUsergroup() : null;
        // 操作地址
        String operateaddr = SessionUtil.getRemoteHost();
        // 调用方法参数类型
        Class<?>[] parametertypes = methodSignature.getParameterTypes();
        List<String> parametertypeList = new ArrayList<>();
        for (Class<?> parametertype : parametertypes) {
        	parametertypeList.add(parametertype.getTypeName());
        }
        // 调用方法
        StringBuilder invokemethodBuilder = new StringBuilder(joinPoint.getSignature().getDeclaringTypeName()).append("#")
    		.append(joinPoint.getSignature().getName()).append("(").append(StringUtils.join(parametertypeList, ", ")).append(")");
        // 请求数据
        Map<String, Object> requestdata = new HashMap<>();
        Enumeration<?> parameterNames = request.getParameterNames();
        while (parameterNames.hasMoreElements()) {
			String parameterName = (String) parameterNames.nextElement();
			requestdata.put(parameterName, request.getParameterValues(parameterName).length > 1 ? mapper.writeValueAsString(request.getParameterValues(parameterName)) : request.getParameter(parameterName));
		}
        // 获取操作日志信息
        OperateLog operateLog = AnnotatedElementUtils.findMergedAnnotation(method, OperateLog.class);
        Operatelogs operatelogs = new Operatelogs(EntityState.VALID.getValue(), operator, usergroup, new Date(), operateaddr, operateLog.value(), 
        		operateLog.type().getValue(), invokemethodBuilder.toString(), null, mapper.writeValueAsString(requestdata), null);
	    return operatelogs;
	}
}
